#include "mag3110.h"


#define MAG3310_WADDR	0x1C
#define MAG3310_RADDR	0x1D

#define MAG3310_DDRA	DDRE0
#define MAG3310_SDAO	PTE0_OUT
#define MAG3310_SDAI	PTE0_INT
#define MAG3310_SCL		PTE1_OUT

#define MAG3310_DELAY	5

/**********************************************************************************************/

/*************************************************************************
*                           蓝宙电子工作室
*
*  函数名称：IIC_MAG3310_Delay_us
*  功能说明：延时程序
*  参数说明：
*  函数返回：无
*  修改时间：2014-9-12
*  备    注：
*************************************************************************/

static void IIC_MAG3310_Delay_us(uint8_t us)                 //1us延时函数
{
    volatile uint8_t i ,j ;
    if(us < 1 )  us = 1 ;
    for(i=0; i<us; i++)
    {
        for(j = 0 ; j < 15 ; j ++);
    }

}

/*************************************************************************
*                           蓝宙电子工作室
*
*  函数名称：IIC_MAG3310_Start
*  功能说明：启动I2C总线子程序
*  参数说明：
*  函数返回：无
*  修改时间：2014-9-12
*  备    注：
*************************************************************************/
static void IIC_MAG3310_Start(void)
{
    MAG3310_SDAO = 1;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时1us
    MAG3310_SCL = 1;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时5us
    MAG3310_SDAO = 0;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);
    MAG3310_SCL = 0;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);
}

/*************************************************************************
*                           蓝宙电子工作室
*
*  函数名称：IIC_MAG3310_Stop
*  功能说明：停止I2C总线数据传送子程序
*  参数说明：
*  函数返回：无
*  修改时间：2014-9-12
*  备    注：
*************************************************************************/
static void IIC_MAG3310_Stop(void)
{
    MAG3310_SDAO = 0;   	   //时钟保持高，数据线从低到高一次跳变，I2C通信停止
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时1us
    MAG3310_SCL = 1;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);
    MAG3310_SDAO = 1;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);
    MAG3310_SCL = 0;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);
}

/*************************************************************************
*                           蓝宙电子工作室
*
*  函数名称：IIC_MAG3310_Slave_ACK
*  功能说明：从机发送应答位子程序
*  参数说明：
*  函数返回：无
*  修改时间：2014-9-12
*  备    注：
*************************************************************************/
static void IIC_MAG3310_Slave_ACK(void)
{
    MAG3310_SDAO = 0;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时5us
    MAG3310_SCL = 1;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);
    MAG3310_SCL = 0;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时5us
    MAG3310_SDAO = 1;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);
}

/*************************************************************************
*                           蓝宙电子工作室
*
*  函数名称：IIC_MAG3310_Slave_NOACK
*  功能说明：从机发送非应答位子程序，迫使数据传输过程结束
*  参数说明：
*  函数返回：无
*  修改时间：2014-9-12
*  备    注：
*************************************************************************/
static void IIC_MAG3310_Slave_NOACK(void)
{
    MAG3310_SDAO = 1;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时5us
    MAG3310_SCL = 1;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);
    MAG3310_SCL = 0;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时5us
    MAG3310_SDAO = 0;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时5us
}

/*************************************************************************
*                           蓝宙电子工作室
*
*  函数名称：IIC_MAG3310_Check_ACK
*  功能说明：主机应答位检查子程序，迫使数据传输过程结束
*  参数说明：
*  函数返回：无
*  修改时间：2014-9-12
*  备    注：
*************************************************************************/
static uint8_t IIC_MAG3310_Check_ACK(void)
{
    uint8_t check ;

    MAG3310_SDAO = 1;
    MAG3310_DDRA = 0 ;
    MAG3310_SDAI = 1 ;

    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时5us
    MAG3310_SCL = 1;
    check = 0;
    if(MAG3310_SDAI == 1)    // 若BFSDA=1 表明非应答，置位非应答标志F0
        check = 1;
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时1us
    MAG3310_SCL = 0;
    MAG3310_DDRA = 1 ;
    MAG3310_SDAO = 1 ;
    return check;
}

/*************************************************************************
*                           蓝宙电子工作室
*
*  函数名称：IIC_MAG3310_SendByte
*  功能说明：发送一个字节
*  参数说明：ch
*  函数返回：无
*  修改时间：2014-9-12
*  备    注：
*************************************************************************/
static void IIC_MAG3310_SendByte(uint8_t ch)
{
    uint8_t n=8;     // 向BFSDA上发送一位数据字节，共八位
    MAG3310_DDRA=1;
    while(n--)
    {
        if((ch&0x80) == 0x80)    // 若要发送的数据最高位为1则发送位1
        {
            MAG3310_SDAO = 1;    // 传送位1
            IIC_MAG3310_Delay_us(MAG3310_DELAY);
            MAG3310_SCL = 1;
            IIC_MAG3310_Delay_us(MAG3310_DELAY);
            MAG3310_SCL = 0;
            IIC_MAG3310_Delay_us(MAG3310_DELAY);

        }
        else
        {
            MAG3310_SDAO = 0;    // 否则传送位0
            IIC_MAG3310_Delay_us(MAG3310_DELAY);
            MAG3310_SCL = 1;
            IIC_MAG3310_Delay_us(MAG3310_DELAY);
            MAG3310_SCL = 0;
            IIC_MAG3310_Delay_us(MAG3310_DELAY);
        }
        ch = ch<<1;    // 数据左移一位
    }
}

/*************************************************************************
*                           蓝宙电子工作室
*
*  函数名称：IIC_MAG3310_ReceiveByte
*  功能说明：接收一字节子程序
*  参数说明： 无
*  函数返回：返回接收的数据
*  修改时间：2014-9-12
*  备    注：
*************************************************************************/
static uint8_t IIC_MAG3310_ReceiveByte(void)
{
    uint8_t n=8;    // 从BFSDA线上读取一上数据字节，共八位
    uint8_t tdata = 0;
    MAG3310_DDRA = 0 ;
    while(n--)
    {
        MAG3310_SDAI = 1;
        IIC_MAG3310_Delay_us(MAG3310_DELAY);
        MAG3310_SCL=0;
        IIC_MAG3310_Delay_us(MAG3310_DELAY);
        MAG3310_SCL = 1;
        IIC_MAG3310_Delay_us(MAG3310_DELAY);
        tdata = tdata<<1;    // 左移一位，或_crol_(temp,1)
        if(MAG3310_SDAI == 1)
            tdata = tdata|0x01;    // 若接收到的位为1，则数据的最后一位置1
        else
            tdata = tdata&0xfe;    // 否则数据的最后一位置0
    }
    MAG3310_SCL=0;
    MAG3310_DDRA = 1;
    return(tdata);
}

/*************************************************************************
*                             蓝宙电子工作室
*
*  函数名称：IIC_WriteAddr
*  功能说明：写入一个字节数据到I2C设备指定寄存器地址
*  参数说明：slave_add     8位写从机地址
*            Addr        从机的寄存器地址
*            Data        数据
*  函数返回：无
*  修改时间：2012-1-20
*  备    注：
*************************************************************************/

static uint8_t IIC_MAG3310_WriteAddr(u8 slave_add, u8 Addr, u8 Data)
{
    uint8_t check = 1;

    IIC_MAG3310_Start();                // 启动I2C

    IIC_MAG3310_SendByte(slave_add);     // 发送地址位
    check = IIC_MAG3310_Check_ACK();                // 检查应答位

    if(check == 1)
    {
        return FALSE;    // 若非应答表明器件错误或已坏，置错误标志位SystemError
    }

    IIC_MAG3310_SendByte(Addr);
    check= IIC_MAG3310_Check_ACK();    // 检查应答位
    if (check == 1)
    {
        return FALSE;    // 若非应答表明器件错误或已坏，置错误标志位SystemError
    }

    IIC_MAG3310_SendByte(Data);
    check= IIC_MAG3310_Check_ACK();    // 检查应答位
    if (check == 1)
    {
        return FALSE;    // 若非应答表明器件错误或已坏，置错误标志位SystemError
    }

    IIC_MAG3310_Stop();         // 全部发完则停止

    return TRUE;
}

/*************************************************************************
*                             蓝宙电子工作室
*
*  函数名称：I2C_ReadAddr
*  功能说明：读取I2C设备指定地址寄存器的数据
*  参数说明：slave_add     8位写从机地址
*            Addr        从机的寄存器地址
*            *rece_data  接收数据数组
*  函数返回：从机寄存器的数据
*  修改时间：2012-1-20
*  备    注：
*************************************************************************/
static uint8_t IIC_MAG3310_ReadAddr_byte(uint8_t slave_add, uint8_t Addr, uint8_t *rece_data)
{
    uint8_t check;

    IIC_MAG3310_Start();                                       //ST
    IIC_MAG3310_SendByte(MAG3310_WADDR);                        // MAG3310_WADDR
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时5us
    check =IIC_MAG3310_Check_ACK();

    if(check == 1)
    {
        return FALSE;
    }

    IIC_MAG3310_SendByte(Addr);                                 //REGISTER ADDR
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时5us
    check =IIC_MAG3310_Check_ACK();
    if(check == 1)
    {
        return FALSE;
    }

    IIC_MAG3310_Start();

    IIC_MAG3310_SendByte(slave_add);
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时1us
    check =IIC_MAG3310_Check_ACK();
    if(check == 1)
    {
        return FALSE;
    }

    *rece_data=IIC_MAG3310_ReceiveByte();

    IIC_MAG3310_Slave_NOACK();    // 收到最后一个字节后发送一个非应答位

    IIC_MAG3310_Stop();
    return TRUE;
}

/*************************************************************************
*                             蓝宙电子工作室
*
*  函数名称：I2C_ReadAddr
*  功能说明：读取I2C设备指定地址寄存器的数据
*  参数说明：slave_add     8位读从机地址
*            Addr        从机的寄存器地址
*            *rece_data  接收数据数组
*            n           数据个数
*  函数返回：从机寄存器的数据
*  修改时间：2012-1-20
*  备    注：
*************************************************************************/
static uint8_t IIC_MAG3310_ReadAddr(uint8_t slave_add, uint8_t Addr, uint8_t *rece_data, uint16_t n)
{
    uint8_t receive_da,check;
    uint16_t i=0;
    uint8_t *rece_data_buff ;
    rece_data_buff = rece_data ;

    IIC_MAG3310_Start();                                       //ST
    IIC_MAG3310_SendByte(MAG3310_WADDR);                        // MAG3310_WADDR
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时1us
    check =IIC_MAG3310_Check_ACK();
    if(check == 1)
    {
        return FALSE;
    }

    IIC_MAG3310_SendByte(Addr);                                 //REGISTER ADDR
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时1us
    check =IIC_MAG3310_Check_ACK();
    if(check == 1)
    {
        return FALSE;
    }

    IIC_MAG3310_Start();

    IIC_MAG3310_SendByte(slave_add);
    IIC_MAG3310_Delay_us(MAG3310_DELAY);      // 延时1us
    check =IIC_MAG3310_Check_ACK();
    if(check == 1)
    {
        return FALSE;
    }

    for(; n > 1; n--)
    {
        receive_da=IIC_MAG3310_ReceiveByte();
        rece_data_buff[i++]=receive_da;
        IIC_MAG3310_Slave_ACK();    // 收到一个字节后发送一个应答位
    }

    receive_da=IIC_MAG3310_ReceiveByte();
    rece_data_buff[i++]=receive_da;
    IIC_MAG3310_Slave_NOACK();    // 收到最后一个字节后发送一个非应答位

    IIC_MAG3310_Stop();
    return TRUE;
}

/**********************************************************************************************/

/*  Max Min Value in each axis  */
static int16_t MAG3110_XMax = -1024;
static int16_t MAG3110_XMin = 1024;
static int16_t MAG3110_YMax = -1024;
static int16_t MAG3110_YMin = 1024;
static int16_t MAG3110_ZMax = -1024;
static int16_t MAG3110_ZMin = 1024;
/*     (Max+Min)/2              */
static int16_t MAG3110_XOff = 0;
static int16_t MAG3110_YOff = 0;
static int16_t MAG3110_ZOff = 0;
/*      Gain                    */
static float   MAG3110_XGain = 1;
static float   MAG3110_YGain = 0;
static float   MAG3110_ZGain = 0;
/***********************************************************************************************
   * @function: MAG3110_WriteRegister
   * @param   : Register  : Register Addr in LCM
                Value     : Value
   * @return  : none
   * @detail  : also known as WriteCommand in some application
************************************************************************************************/
static void MAG3110_WriteRegister(uint8_t Register, uint8_t Value)
{
    IIC_MAG3310_WriteAddr(MAG3310_WADDR,Register,Value);
}
/***********************************************************************************************
   * @function: MAG3110_ReadRegister
   * @param   : Register  : Register Addr in LCM
   * @return  : Register Value
   * @detail  : none
************************************************************************************************/
static uint8_t  MAG3110_ReadRegister(uint8_t Register)
{
	uint8_t result = 0;
    IIC_MAG3310_ReadAddr_byte(MAG3310_RADDR,Register,&result);
    return result;
}

/***********************************************************************************************
   * @function: MAG3110_GetXYZData
   * @param   : MAG3110_XYZDataTypeDef  : DataStruct for MAG3110
   * @return  : TRUE: Succ  FALSE:Err
   * @detail  : none
************************************************************************************************/
uint8_t MAG3110_L_GetXYZData(MAG3110_XYZDataTypeDef* MAG3110_XYZDataStruct)
{
    uint8_t ret;
    ret = MAG3110_ReadRegister(MAG_DR_STATUS);
    if(ret & MAG_DR_STATUS_ZYXDR_MASK)
    {
        MAG3110_XYZDataStruct->m_XData = MAG3110_ReadRegister(MAG_OUT_X_LSB) + 256*MAG3110_ReadRegister(MAG_OUT_X_MSB);
        MAG3110_XYZDataStruct->m_YData = MAG3110_ReadRegister(MAG_OUT_Y_LSB) + 256*MAG3110_ReadRegister(MAG_OUT_Y_MSB);
        MAG3110_XYZDataStruct->m_ZData = MAG3110_ReadRegister(MAG_OUT_Z_LSB) + 256*MAG3110_ReadRegister(MAG_OUT_Z_MSB);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}
/***********************************************************************************************
   * @function: MAG3110_GetAngle
   * @param   : MAG3110_XYZDataTypeDef  : DataStruct for MAG3110
   * @return  : TRUE: Succ  FALSE:Err
   * @detail  : none
************************************************************************************************/
uint8_t MAG3110_L_GetAngle(uint16_t* res)
{
    int16_t x,y,z;
    int16_t angle;
    MAG3110_XYZDataTypeDef MAG3110_XYZDataStruct1;
    if(MAG3110_L_GetXYZData(&MAG3110_XYZDataStruct1) == TRUE)
    {
        x = MAG3110_XYZDataStruct1.m_XData;
        y = MAG3110_XYZDataStruct1.m_YData;
        z = MAG3110_XYZDataStruct1.m_ZData;
        /*  find if MAG3110 is calibrated, if not, use deafault macro  */
        if((MAG3110_XOff == 0) && (MAG3110_YOff == 0) && (MAG3110_ZOff == 0)) //no cal ,use deafalut value
        {
            x-= MAG3110_DEFAULT_XOFF;
            y-= MAG3110_DEFAULT_YOFF;
            z-= MAG3110_DEFAULT_ZOFF;
            MAG3110_XGain = MAG3110_DEFAULT_XGAIN;
            MAG3110_YGain = MAG3110_DEFAULT_YGAIN;
            MAG3110_ZGain = MAG3110_DEFAULT_ZGAIN;
            x = ((float)x*MAG3110_XGain);
            y = ((float)y*MAG3110_YGain);
            z = ((float)z*MAG3110_ZGain);
        }
        else //caled ,use real-time cal value
        {
            x-= MAG3110_XOff;
            y -= MAG3110_YOff;
            z -= MAG3110_ZOff;
            x = ((float)x*MAG3110_XGain);
            y = ((float)y*MAG3110_YGain);
            z = ((float)z*MAG3110_ZGain);
        }

        angle = (atan2((float)y,(float)x)*180/3.14) + 180;
        *res = angle;
        return TRUE;
    }
    return FALSE;
}
/***********************************************************************************************
   * @function: MAG3110_StartCalibrateOnce
   * @param   : Calibrate
   * @return  : TRUE: Succ  FALSE:Err
   * @detail  : this function need to call many times to calibrate
************************************************************************************************/
uint8_t MAG3110_L_StartCalibrateOnce(void)
{
    MAG3110_XYZDataTypeDef MAG3110_XYZDataStruct1;
    uint16_t abs_XRangle = 0;
    uint16_t abs_YRangle = 0;
    uint16_t abs_ZRangle = 0;
    if(MAG3110_L_GetXYZData(&MAG3110_XYZDataStruct1) == TRUE)
    {
        //X
        if(MAG3110_XYZDataStruct1.m_XData > MAG3110_XMax)
        {
            MAG3110_XMax = MAG3110_XYZDataStruct1.m_XData;
        }
        if(MAG3110_XYZDataStruct1.m_XData < MAG3110_XMin)
        {
            MAG3110_XMin = MAG3110_XYZDataStruct1.m_XData;
        }
        //Y
        if(MAG3110_XYZDataStruct1.m_YData > MAG3110_YMax)
        {
            MAG3110_YMax = MAG3110_XYZDataStruct1.m_YData;
        }
        if(MAG3110_XYZDataStruct1.m_YData < MAG3110_YMin)
        {
            MAG3110_YMin = MAG3110_XYZDataStruct1.m_YData;
        }
        //Z
        if(MAG3110_XYZDataStruct1.m_ZData > MAG3110_ZMax)
        {
            MAG3110_ZMax = MAG3110_XYZDataStruct1.m_ZData;
        }
        if(MAG3110_XYZDataStruct1.m_ZData < MAG3110_ZMin)
        {
            MAG3110_ZMin = MAG3110_XYZDataStruct1.m_ZData;
        }
        MAG3110_XOff = (MAG3110_XMax + MAG3110_XMin)/2;
        MAG3110_YOff = (MAG3110_YMax + MAG3110_YMin)/2;
        MAG3110_ZOff = (MAG3110_ZMax + MAG3110_ZMin)/2;

        MAG3110_XGain = 1;
        abs_XRangle = abs(MAG3110_XMax - MAG3110_XMin);
        abs_YRangle = abs(MAG3110_YMax - MAG3110_YMin);
        abs_ZRangle = abs(MAG3110_ZMax - MAG3110_ZMin);
        MAG3110_YGain = MAG3110_XGain*((float)abs_YRangle/(float)abs_XRangle);
        MAG3110_ZGain = MAG3110_XGain*((float)abs_ZRangle/(float)abs_XRangle);
        /* you may display MAG3110_XOff yoff zff ,ygain zgain here for use  */
        return TRUE;
    }
    return FALSE;
}

/* Initalize MAG3110     */
uint8_t MAG3110_L_Init(void)
{
    uint8_t RegisterValue = 0;

    /* Initalize Kinetis I2C module  */
    //I2C_init(MAG3110_I2C_BASE_PTR);
	gpio_init (PTE0, GPO, HIGH);     //初始化I2C时钟引脚
	gpio_init (PTE1, GPO, HIGH);    //初始化I2C数据引脚

    /* see if master can read WHO_AM_I correct*/
    RegisterValue = MAG3110_ReadRegister(MAG_WHO_AM_I);
    if(RegisterValue != MAG_WHO_AM_I_VALUE)
    {
        return FALSE;
    }
    /*  set common value     */
    MAG3110_WriteRegister(MAG_CTRL_REG1,0x00);
    MAG3110_WriteRegister(MAG_CTRL_REG1,0x19); //10Hz
    MAG3110_WriteRegister(MAG_CTRL_REG2,0x80);

    return TRUE;
}
